using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

using xWinFormsLib;

namespace AnakraEditor
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        int i = 0;
        int selectedItem = -1;
        bool rmbLastDown;
        bool mmbLastDown;
        bool lmbLastDown;
        float speed = 0.01f;
        Vector2 offset;
        float offsetRot;
        float offsetScale;
        Vector2 offsetMouse = new Vector2(0,0);
        Vector2 objectOffset = new Vector2(0, 0);
        int cellWidth = 64;
        int cellHeight = 64;
        float rotationSnap = 0.5f;
        float scaleSnap = 0.2f;
        bool enterLastDown;
        bool snapping;
        Item item;
        float f = 0;
        float scaleOffset = 1.0f;

        int currTex;
        List<Texture2D> editorTextures = new List<Texture2D>();

        FormCollection formCollection;

        Scene scene;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            this.Window.Title = "XNA LED";
            this.Window.AllowUserResizing = true;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        public void LoadPressed(Object o, EventArgs e)
        {
            scene.Load(formCollection["Window"]["LevelPath"].Text);
            ((Listbox)formCollection["Window"]["LoadedTextures"]).Items = scene.texturePaths;
            editorTextures = new List<Texture2D>();
            i = 1;
            while (i < scene.texturePaths.Count)
            {
                editorTextures.Add(Texture2D.FromFile(graphics.GraphicsDevice, scene.texturePaths[i]));
                i++;
            }
        }

        public void SavePressed(Object o, EventArgs e)
        {
            scene.texturePaths = new List<string>();
            i = 0;
            while (i < ((Listbox)formCollection["Window"]["LoadedTextures"]).Items.Count)
            {
                scene.texturePaths.Add(((Listbox)formCollection["Window"]["LoadedTextures"]).Items[i]);
                i++;
            }
            scene.Save(formCollection["Window"]["LevelPath"].Text);
        }

        public void LoadImagePressed(Object o, EventArgs e)
        {
            editorTextures.Add(Texture2D.FromFile(graphics.GraphicsDevice, formCollection["Window"].Controls["ImagePath"].Text));
            ((Listbox)formCollection["Window"]["LoadedTextures"]).Items.Add(formCollection["Window"].Controls["ImagePath"].Text);
        }

        public void UseTexturePressed(Object o, EventArgs e)
        {
            currTex = ((Listbox)formCollection["Window"]["LoadedTextures"]).SelectedIndex-1;
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            formCollection = new FormCollection(this.Window, Services, ref graphics);

            formCollection.Add(new Form("Window", "Toolbox", new Vector2(350, 225), new Vector2(10, 10), Form.BorderStyle.Sizable));
            formCollection["Window"].Style = Form.BorderStyle.Sizable;

            formCollection["Window"].Controls.Add(new Textbox("LevelPath", new Vector2(15, 30), 150, 15, "MyLevel.xml"));

            formCollection["Window"].Controls.Add(new Button("LoadLevel", new Vector2(15, 55), 70, "Load", Color.White, Color.Black));
            formCollection["Window"]["LoadLevel"].OnPress += LoadPressed;

            formCollection["Window"].Controls.Add(new Button("SaveLevel", new Vector2(95, 55), 70, "Save", Color.White, Color.Black));
            formCollection["Window"]["SaveLevel"].OnPress += SavePressed;

            /*////////////////////
            //Property Editor
            formCollection["Window"].Controls.Add(new Label("Attribute1Label", new Vector2(15, 85), "Id", Color.White, Color.Black, 60, Label.Align.Left));
            formCollection["Window"].Controls.Add(new Textbox("Attribute1", new Vector2(85, 85), 80, 15, ""));

            formCollection["Window"].Controls.Add(new Label("Attribute2Label", new Vector2(15, 110), "Texture", Color.White, Color.Black, 60, Label.Align.Left));
            formCollection["Window"].Controls.Add(new Textbox("Attribute2", new Vector2(85, 110), 80, 15, ""));

            formCollection["Window"].Controls.Add(new Label("Attribute3Label", new Vector2(15, 135), "Depth", Color.White, Color.Black, 60, Label.Align.Left));
            formCollection["Window"].Controls.Add(new Textbox("Attribute3", new Vector2(85, 135), 80, 15, ""));

            formCollection["Window"].Controls.Add(new Label("Attribute4Label", new Vector2(15, 160), "Visible", Color.White, Color.Black, 60, Label.Align.Left));
            formCollection["Window"].Controls.Add(new Textbox("Attribute4", new Vector2(85, 160), 80, 15, ""));

            formCollection["Window"].Controls.Add(new Label("Attribute5Label", new Vector2(15, 185), "Attributes", Color.White, Color.Black, 60, Label.Align.Left));
            formCollection["Window"].Controls.Add(new Textbox("Attribute5", new Vector2(85, 185), 80, 15, ""));
            //End Property Ed
            ////////////////////*/

            formCollection["Window"].Controls.Add(new Textbox("ImagePath", new Vector2(175, 30), 150, 15, "MyImage.png"));

            formCollection["Window"].Controls.Add(new Button("LoadImage", new Vector2(175, 55), 130, "Load image", Color.White, Color.Black));
            formCollection["Window"]["LoadImage"].OnPress += LoadImagePressed;

            formCollection["Window"].Controls.Add(new Listbox("LoadedTextures", new Vector2(175, 80), 150, 97, new string[]{"Default"}));
            
            formCollection["Window"].Controls.Add(new Button("UseTexture", new Vector2(175, 185), 150, "Place selected texture", Color.White, Color.Black));
            formCollection["Window"]["UseTexture"].OnPress += UseTexturePressed;

            formCollection["Window"].Show();

            formCollection["Window"].Focus();

            scene = new Scene();

            
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here

            formCollection.Dispose();
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            

            if (Mouse.GetState().MiddleButton == ButtonState.Pressed && !mmbLastDown)
            {
                if (currTex >= 0)
                {
                    item = new Item();
                    item.id = "Item";
                    item.texture = currTex;
                    if (snapping)
                    {
                        item.position.y = (float)Math.Round((double)((Mouse.GetState().Y - objectOffset.Y) / cellHeight)) * cellHeight;
                        item.position.x = (float)Math.Round((double)((Mouse.GetState().X - objectOffset.X) / cellWidth)) * cellWidth;
                    }
                    else
                    {
                        item.position.x = Mouse.GetState().X - objectOffset.X;
                        item.position.y = Mouse.GetState().Y - objectOffset.Y;
                    }
                    item.rotation = 0;
                    item.depth = 1.0f;
                    item.scale = 1.0f;
                    item.visible = true;
                    item.attributes = "";
                    scene.items.Add(item);
                }
            }
            else if (Mouse.GetState().RightButton == ButtonState.Pressed && !rmbLastDown)
            {
                if (!(Mouse.GetState().LeftButton == ButtonState.Pressed))
                {
                    i = 0;
                    selectedItem = -1;
                    while (i < scene.items.Count)
                    {
                        if ((new Rectangle((int)scene.items[i].position.x - ((int)editorTextures[scene.items[i].texture].Width / 2), (int)scene.items[i].position.y - ((int)editorTextures[scene.items[i].texture].Height / 2), (int)editorTextures[scene.items[i].texture].Width, (int)editorTextures[scene.items[i].texture].Height)).Contains(new Point(Mouse.GetState().X - (int)objectOffset.X, Mouse.GetState().Y - (int)objectOffset.Y)))
                        {
                            selectedItem = i;
                            /*((Textbox)formCollection["Window"]["Attribute1"]).Text = scene.items[i].id;
                            ((Textbox)formCollection["Window"]["Attribute2"]).Text = scene.items[i].texture.ToString();
                            ((Textbox)formCollection["Window"]["Attribute3"]).Text = scene.items[i].depth.ToString();
                            ((Textbox)formCollection["Window"]["Attribute4"]).Text = scene.items[i].visible.ToString();
                            ((Textbox)formCollection["Window"]["Attribute5"]).Text = scene.items[i].attributes;
                            */break;
                        }
                        i++;
                    }
                }
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Delete))
            {
                if (selectedItem >= 0)
                {
                    scene.items.RemoveAt(selectedItem);
                    selectedItem = -1;
                }
            }
            if (Keyboard.GetState().IsKeyDown(Keys.LeftShift) || Keyboard.GetState().IsKeyDown(Keys.RightShift))
            {
                speed = 0.05f;
            }
            else
            {
                speed = 0.01f;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Up))
            {
                if (selectedItem >= 0)
                {
                    Item item = scene.items[selectedItem];
                    item.position.y -= speed * (float)gameTime.ElapsedGameTime.Milliseconds;
                    scene.items[selectedItem] = item;
                }

            }
            if (Keyboard.GetState().IsKeyDown(Keys.Down))
            {
                if (selectedItem >= 0)
                {
                    Item item = scene.items[selectedItem];
                    item.position.y += speed * (float)gameTime.ElapsedGameTime.Milliseconds;
                    scene.items[selectedItem] = item;
                }

            }
            if (Keyboard.GetState().IsKeyDown(Keys.Left))
            {
                if (selectedItem >= 0)
                {
                    Item item = scene.items[selectedItem];
                    item.position.x -= speed * (float)gameTime.ElapsedGameTime.Milliseconds;
                    scene.items[selectedItem] = item;
                }

            }
            if (Keyboard.GetState().IsKeyDown(Keys.Right))
            {
                if (selectedItem >= 0)
                {
                    Item item = scene.items[selectedItem];
                    item.position.x += speed * (float)gameTime.ElapsedGameTime.Milliseconds;
                    scene.items[selectedItem] = item;
                }

            }

            if (Mouse.GetState().LeftButton == ButtonState.Pressed)
            {
                if (selectedItem >= 0)
                {
                    if (lmbLastDown)
                    {
                        offsetMouse = new Vector2(0, 0);
                        if (Keyboard.GetState().IsKeyDown(Keys.RightAlt) || Keyboard.GetState().IsKeyDown(Keys.LeftAlt))
                        {
                            Item item = scene.items[selectedItem];
                            item.rotation = (float)Math.Atan2((Mouse.GetState().Y - objectOffset.Y) - item.position.y, (Mouse.GetState().X - objectOffset.X) - item.position.x) + offsetRot;
                            if (snapping) item.rotation = (float)Math.Round((double)(item.rotation / rotationSnap)) * rotationSnap;
                            scene.items[selectedItem] = item;
                        }
                        else if (Keyboard.GetState().IsKeyDown(Keys.RightControl) || Keyboard.GetState().IsKeyDown(Keys.LeftControl))
                        {
                            Item item = scene.items[selectedItem];
                            item.scale = ((float)Math.Sqrt((double)(((Mouse.GetState().X-objectOffset.X) - scene.items[selectedItem].position.x) * ((Mouse.GetState().X-objectOffset.X) - scene.items[selectedItem].position.x)) + (((Mouse.GetState().Y-objectOffset.Y) - scene.items[selectedItem].position.y) * ((Mouse.GetState().Y-objectOffset.Y) - scene.items[selectedItem].position.y)))) / offsetScale;
                            if (snapping) item.scale = (float)Math.Round((double)(item.scale / scaleSnap)) * scaleSnap;
                            scene.items[selectedItem] = item;
                        }
                        else if (Keyboard.GetState().IsKeyDown(Keys.RightShift) || Keyboard.GetState().IsKeyDown(Keys.LeftShift))
                        {
                            selectedItem = -1;
                            objectOffset += new Vector2(Mouse.GetState().X - offsetMouse.X, Mouse.GetState().Y - offsetMouse.Y);
                            offsetMouse = new Vector2(Mouse.GetState().X, Mouse.GetState().Y);
                        }
                        else
                        {
                            Item item = scene.items[selectedItem];
                            item.position.x = Mouse.GetState().X + objectOffset.X+offset.X;
                            item.position.y = Mouse.GetState().Y + objectOffset.Y+offset.Y;
                            if (snapping)
                            {
                                item.position.y = (float)Math.Round((double)(item.position.y / cellHeight)) * cellHeight;
                                item.position.x = (float)Math.Round((double)(item.position.x / cellWidth)) * cellWidth;
                            }
                            scene.items[selectedItem] = item;
                        }
                    }
                    else
                    {
                        if (Keyboard.GetState().IsKeyDown(Keys.RightAlt) || Keyboard.GetState().IsKeyDown(Keys.LeftAlt))
                        {
                            offsetRot = scene.items[selectedItem].rotation - (float)Math.Atan2((Mouse.GetState().Y-objectOffset.Y) - scene.items[selectedItem].position.y, (Mouse.GetState().X-objectOffset.X) - scene.items[selectedItem].position.x);
                        }
                        else if (Keyboard.GetState().IsKeyDown(Keys.RightControl) || Keyboard.GetState().IsKeyDown(Keys.LeftControl))
                        {
                            offsetScale = (float)Math.Sqrt((double)(((Mouse.GetState().X-objectOffset.X) - scene.items[selectedItem].position.x) * ((Mouse.GetState().X-objectOffset.X) - scene.items[selectedItem].position.x)) + (((Mouse.GetState().Y - objectOffset.Y) - scene.items[selectedItem].position.y) * ((Mouse.GetState().Y-objectOffset.Y) - scene.items[selectedItem].position.y)));
                        }
                        else if (Keyboard.GetState().IsKeyDown(Keys.RightShift) || Keyboard.GetState().IsKeyDown(Keys.LeftShift))
                        {
                            selectedItem = -1;
                            offsetMouse = new Vector2(Mouse.GetState().X, Mouse.GetState().Y);
                        }
                        else
                        {
                            offset = new Vector2(scene.items[selectedItem].position.x - (Mouse.GetState().X+objectOffset.X), scene.items[selectedItem].position.y - (Mouse.GetState().Y + objectOffset.Y));
                        }
                    }
                }
                else
                {
                    if (lmbLastDown)
                    {
                        if (Keyboard.GetState().IsKeyDown(Keys.RightShift) || Keyboard.GetState().IsKeyDown(Keys.LeftShift))
                        {
                            objectOffset += new Vector2(Mouse.GetState().X - offsetMouse.X, Mouse.GetState().Y - offsetMouse.Y);
                            offsetMouse = new Vector2(Mouse.GetState().X, Mouse.GetState().Y);
                        }
                    }
                    else
                    {
                        if (Keyboard.GetState().IsKeyDown(Keys.RightShift) || Keyboard.GetState().IsKeyDown(Keys.LeftShift))
                        {
                            offsetMouse = new Vector2(Mouse.GetState().X, Mouse.GetState().Y);
                        }
                    }
                }
                lmbLastDown = true;
            }
            else
            {
                lmbLastDown = false;
            }

            if(Keyboard.GetState().IsKeyDown(Keys.Enter) && !enterLastDown) {
                snapping = !snapping;
            }

            enterLastDown = Keyboard.GetState().IsKeyDown(Keys.Enter);
            mmbLastDown = (Mouse.GetState().MiddleButton == ButtonState.Pressed);
            rmbLastDown = (Mouse.GetState().LeftButton == ButtonState.Pressed);

            //scaleOffset = 1+Math.Abs(Mouse.GetState().ScrollWheelValue) - f;
            //f = Math.Abs(Mouse.GetState().ScrollWheelValue);

            formCollection.Update(gameTime);

            /*if (selectedItem > 0)
            {
                if (((Textbox)formCollection["Window"]["Attribute1"]).Text != scene.items[i].id)
                {
                    ((Textbox)formCollection["Window"]["Attribute1"]).Text = scene.items[i].id;
                }
                else
                {
                    item  = scene.items[selectedItem];
                    item.id = ((Textbox)formCollection["Window"]["Attribute1"]).Text;
                    scene.items[selectedItem] = item;
                }
                if (((Textbox)formCollection["Window"]["Attribute2"]).Text != scene.items[i].texture.ToString())
                {
                    ((Textbox)formCollection["Window"]["Attribute2"]).Text = scene.items[i].texture.ToString();
                }
                else
                {
                    item = scene.items[selectedItem];
                    item.texture = ((Textbox)formCollection["Window"]["Attribute2"]).Text.ToCharArray()[0];
                    //scene.items[selectedItem] = item;
                }
                if (((Textbox)formCollection["Window"]["Attribute3"]).Text != scene.items[i].depth.ToString())
                {
                    ((Textbox)formCollection["Window"]["Attribute3"]).Text = scene.items[i].depth.ToString();
                }
                else
                {
                    item = scene.items[selectedItem];
                    //item.depth = ((Textbox)formCollection["Window"]["Attribute3"]).Text.ToCharArray()[1];
                    //scene.items[selectedItem] = item;
                }
                if (((Textbox)formCollection["Window"]["Attribute4"]).Text != scene.items[i].visible.ToString())
                {
                    ((Textbox)formCollection["Window"]["Attribute4"]).Text = scene.items[i].visible.ToString();
                }
                else
                {
                    item = scene.items[selectedItem];
                    if ((((Textbox)formCollection["Window"]["Attribute4"]).Text.ToCharArray()[1] == 'f') || (((Textbox)formCollection["Window"]["Attribute1"]).Text.ToCharArray()[1] == 'F'))
                    {
                        item.visible = false;
                    }
                    else if ((((Textbox)formCollection["Window"]["Attribute4"]).Text.ToCharArray()[1] == 't') || (((Textbox)formCollection["Window"]["Attribute1"]).Text.ToCharArray()[1] == 'T'))
                    {
                        item.visible = true;
                    }
                    scene.items[selectedItem] = item;
                }
                if (((Textbox)formCollection["Window"]["Attribute5"]).Text != scene.items[i].attributes)
                {
                    ((Textbox)formCollection["Window"]["Attribute5"]).Text = scene.items[i].attributes;
                }
                else
                {
                    item = scene.items[selectedItem];
                    item.attributes = ((Textbox)formCollection["Window"]["Attribute5"]).Text;
                    scene.items[selectedItem] = item;
                }
            }*/


            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            formCollection.Render();

            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.BackToFront, SaveStateMode.SaveState);
            i = 0;
            while(i<scene.items.Count) {
                if (i != selectedItem)
                {
                    spriteBatch.Draw(editorTextures[scene.items[i].texture], new Vector2(scene.items[i].position.x + objectOffset.X, scene.items[i].position.y + objectOffset.Y), null, Color.White, scene.items[i].rotation, new Vector2(editorTextures[scene.items[i].texture].Width / 2, editorTextures[scene.items[i].texture].Height / 2), scene.items[i].scale * scaleOffset, SpriteEffects.None, scene.items[i].depth);
                }
                else
                {
                    spriteBatch.Draw(editorTextures[scene.items[i].texture], new Vector2(scene.items[i].position.x + objectOffset.X, scene.items[i].position.y + objectOffset.Y), null, Color.Red, scene.items[i].rotation, new Vector2(editorTextures[scene.items[i].texture].Width / 2, editorTextures[scene.items[i].texture].Height / 2), scene.items[i].scale * scaleOffset, SpriteEffects.None, scene.items[i].depth);
                }
                i++;
            }
            spriteBatch.End();

            formCollection.Draw();

            base.Draw(gameTime);
        }
    }
}
